package com.moon.back.annotation;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.moon.back.exception.DebounceException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

//@EnableAspectJAutoProxy
@Slf4j
@Aspect
@Component
public class DebounceAspect {
    private final Cache<String, Long> cache = Caffeine.newBuilder()
        .expireAfterWrite(1, TimeUnit.HOURS)
        .maximumSize(1000)
        .build();
    private final ExpressionParser parser = new SpelExpressionParser();
    @Around("@annotation(debounce)")
    public Object debounce(ProceedingJoinPoint jp, Debounce debounce) throws Throwable {
        var cacheKey = generateCacheKey(jp, debounce);
        var waitMillis = debounce.unit().toMillis(debounce.waiting());
        var currentTime = System.currentTimeMillis();
        // 通过异步机制完成缓存的刷新操作，对缓存代码快加锁的限制，对并发问题有一定的解决起到一定作用
        synchronized (this) {
            var lastCall = cache.getIfPresent(cacheKey);
            // 使用缓存
            if (lastCall != null && currentTime - lastCall < waitMillis) {
                return handleDebounced(jp, debounce.strategy());
            }
            cache.put(cacheKey, currentTime);
            return jp.proceed();
        }
    }

    private void validateDebounceConfig(Debounce debounce) {
        if (debounce.waiting() <= 0) {
            throw new IllegalArgumentException("防抖时间必须大于0");
        }
        if (debounce.unit().toMillis(debounce.waiting()) > 3600000) {
            throw new IllegalArgumentException("防抖时间不能超过1小时");
        }
    }

    private Object handleDebounced(ProceedingJoinPoint jp, Debounce.Strategy strategy) throws DebounceException {
        if (strategy == Debounce.Strategy.FIRST) {
            return null;
        }
        // LAST策略需要特殊处理（需队列管理） 最后的操作
        throw new DebounceException("操作过于频繁");
    }

    private String generateCacheKey(ProceedingJoinPoint jp, Debounce debounce) {
        // 安全解析SpEL
        try {
            return debounce.key().isEmpty() ?
                    defaultKey(jp) :
                    parseSpEL(jp, debounce.key());
        } catch (Exception e) {
            log.warn("SpEL解析失败，使用默认key", e);
            return defaultKey(jp);
        }
    }

    private String defaultKey(ProceedingJoinPoint jp) {
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        return method.getDeclaringClass().getName() + "#" + method.getName();
    }

    private String parseSpEL(ProceedingJoinPoint jp, String spEL) {
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = jp.getArgs();
        String[] paramNames = ((MethodSignature) jp.getSignature()).getParameterNames();

        for (int i = 0; i < paramNames.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }

        return parser.parseExpression(spEL)
                .getValue(context, String.class);
    }
}